/* 
 * Copyright (c) 2004-2005 Massachusetts Institute of Technology. This code was
 * developed as part of the Haystack (http://haystack.lcs.mit.edu/) research 
 * project at MIT. Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation files (the 
 * "Software"), to deal in the Software without restriction, including without 
 * limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom
 * the Software is furnished to do so, subject to the following conditions: 
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software. 
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE. 
 */
package edu.mit.csail.relo.parts;

import java.io.IOException;
import java.util.Map;

import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.MidpointLocator;
import org.eclipse.draw2d.PolylineConnection;
import org.eclipse.jface.resource.ImageDescriptor;
import org.openrdf.model.Resource;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.rio.RdfDocumentWriter;
import org.openrdf.sesame.sail.StatementIterator;
import org.openrdf.vocabulary.RDF;

import edu.mit.csail.relo.ReloCore;
import edu.mit.csail.relo.agent.BrowseModel;
import edu.mit.csail.relo.eclipse.gef.RoundedPolylineConnection;
import edu.mit.csail.relo.modelBridge.ArtifactRel;
import edu.mit.csail.relo.modelBridge.NamedRel;
import edu.mit.csail.relo.parts.NavAidsEditPolicy.NavAidsRelEditPart;
import edu.mit.csail.relo.store.ReloRdfRepository;
import edu.mit.csail.relo.store.StoreUnionRepository;
import edu.mit.csail.relo.store.StoreUtil;
import edu.mit.csail.relo.ui.ReloEditor;
import edu.mit.csail.relo.utils.LoadUtils;

/**
 * Support for RDF
 * Note: Both source and targe have to be ArtifactEditPart
 */
public class ArtifactRelEditPart extends AbstractReloRelationPart implements NavAidsRelEditPart {
    
	public BrowseModel getBrowseModel() {
		return ((ReloController) getRoot().getContents()).bm;
	}

    protected Resource viewRes = null;
    public Resource getViewRes() {
        if (viewRes==null) viewRes = getBrowseModel().getRepo().createBNode();
        return viewRes;
    }
    public void setViewRes(Resource viewRes) {
        this.viewRes = viewRes;
    }
    

    public ArtifactRel getArtifactRel() {
        return (ArtifactRel) getModel();
    }
    
    public String getRelationLabel() {
        return getRelationLabel(getBrowseModel().getRepo(), getArtifactRel().getType());
    }

    public String getRelationLabel(ReloRdfRepository repo, URI relType) {
        Value lblVal = repo
                        .getStatement(relType, ReloRdfRepository.rdfsLabel, null)
                        .getObject(); 
        if (lblVal != null)
            return lblVal.toString();
        else
            return "";
    }

    /**
     * @see org.eclipse.gef.editparts.AbstractConnectionEditPart#createFigure()
     */
    @Override
    protected IFigure createFigure() {
        PolylineConnection conn = (PolylineConnection) super.createFigure();
        conn.setToolTip(new Label(" " + getRelationLabel() + " "));
        return conn;
    }

    
    public static void writeRDF(ArtifactRelEditPart arep, RdfDocumentWriter rdfWriter) throws IOException {
        rdfWriter.writeStatement(arep.getViewRes(), StoreUtil.createMemURI(RDF.TYPE), ReloCore.link);
        rdfWriter.writeStatement(arep.getViewRes(), ReloCore.model, arep.getArtifactRel().getType());
        rdfWriter.writeStatement(arep.getViewRes(), StoreUtil.createMemURI(RDF.SUBJECT), ((ArtifactEditPart)arep.getSource()).getViewRes());
        rdfWriter.writeStatement(arep.getViewRes(), StoreUtil.createMemURI(RDF.OBJECT), ((ArtifactEditPart)arep.getTarget()).getViewRes());

        arep.writeRDF(rdfWriter);
    }

    /**
     * @param rdfWriter
     * @throws IOException
     */
    protected void writeRDF(RdfDocumentWriter rdfWriter) throws IOException {
    }

    public static ArtifactRelEditPart readRDF(ReloController vc, 
            ReloRdfRepository docRepo, 
            Map<Resource,ArtifactEditPart> viewRes2ArtifactEPMap, 
            Resource viewRes) {
        URI linkRes =(URI) docRepo.getStatement(viewRes, ReloCore.model, null).getObject(); 
        
        // if type is in document, use it to load class otherwise re-create artifact based on the browse model
        Resource docLinkResType = (Resource) docRepo.getStatement(linkRes, docRepo.rdfType, null).getObject();
        ArtifactEditPart srcAEP = viewRes2ArtifactEPMap.get( docRepo.getStatement(viewRes, StoreUtil.createMemURI(RDF.SUBJECT), null).getObject() );
        ArtifactEditPart dstAEP = viewRes2ArtifactEPMap.get( docRepo.getStatement(viewRes, StoreUtil.createMemURI(RDF.OBJECT), null).getObject() );
        ArtifactRel artRel = null;
        if (docLinkResType != null) {
            // use type in document, for loading model class from the main repo
            artRel = (ArtifactRel) StoreUtil.loadClass(vc.bm.getRepo(), docLinkResType, ReloCore.model);
            artRel.init(srcAEP, dstAEP, linkRes);
        } else {
        	if(ReloCore.namedRel.equals(linkRes)){
        		artRel = new NamedRel();
        		artRel.init(srcAEP, dstAEP, linkRes);
        	}else
        		artRel = new ArtifactRel(srcAEP, dstAEP, linkRes);
        }

        ArtifactRelEditPart arep = vc.addRel(srcAEP, artRel, dstAEP);
        arep.setViewRes(viewRes);
        
        // find the source and destination resources and check whether the link between them still exists
        Resource srcNode = (Resource) docRepo.getStatement(viewRes, StoreUtil.createMemURI(RDF.SUBJECT), null).getObject();
        Resource dstNode = (Resource) docRepo.getStatement(viewRes, StoreUtil.createMemURI(RDF.OBJECT), null).getObject();
        Resource modelResSrc = (Resource) docRepo.getStatement(srcNode, ReloCore.model, null).getObject();
        Resource modelResDst = (Resource) docRepo.getStatement(dstNode, ReloCore.model, null).getObject();
        if(LoadUtils.isOutdated(StoreUtil.getDefaultStoreRepository(), modelResSrc, linkRes, modelResDst, vc.getReloEditorResource())) {
        	LoadUtils.addErrors(vc.getReloEditorResource(), modelResSrc, linkRes);
        	ReloEditor editor = vc.getReloEditor();
        	if (editor != null) editor.addErrorDecoration();
        }
        
        //Add the file's relation related statements to the cache repo 
        //so that the diagram can be drawn the way it was saved (even if 
        //its code elements have changed)
        ReloRdfRepository fileStmts = ((StoreUnionRepository)vc.getBrowseModel().getRepo()).getCacheRepo();
        fileStmts.startTransaction();
        fileStmts.addStatement(modelResSrc, linkRes, modelResDst);
        fileStmts.commitTransaction();
        
        arep.readRDF(docRepo);

        return arep;
    }

    /**
     * @param rdfWriter
     * @throws IOException
     */
    protected void readRDF(ReloRdfRepository queryRepo) {
    	((ReloController) getRoot().getContents()).getLayoutMgr().anchorPart(this);
    }
    
    @Override
    public void refreshVisuals() {
    	IFigure connFigure = getFigure();
    	RoundedPolylineConnection conn = null;
		if(connFigure instanceof RoundedPolylineConnection) {
			conn = (RoundedPolylineConnection)connFigure;
		}
    	if(conn==null) return;
    	    	
    	ArtifactRel artRel = getArtifactRel();
    	Resource src = artRel.getSrc().elementRes;
    	Resource dst = artRel.getDest().elementRes;
    	URI link = artRel.getType();
    	
        StatementIterator it = StoreUtil.getDefaultStoreRepository().getStatements(src, link, dst);
		if (it.hasNext()) {
			removeRelationErrDecoration(this, conn);
		} else {
			addRelationErrDecoration(this, conn);
		}
    }
    
    private static int relLabelIndex = 0;
    private static void addRelationErrDecoration(ArtifactRelEditPart arep, RoundedPolylineConnection conn) {
    	//Don't add decoration if it's already been added
    	if(conn.getChildren().get(relLabelIndex) instanceof Label) return;
    	
    	MidpointLocator labelLocator = new MidpointLocator(conn, 0);
    	Label label = new Label(ImageDescriptor.createFromFile(ReloEditor.class, "error_co.gif").createImage());
    	label.setOpaque(false);
    	conn.add(label, labelLocator, relLabelIndex);
    	arep.setFigure(conn);
    }
    private static void removeRelationErrDecoration(ArtifactRelEditPart arep, RoundedPolylineConnection conn) {
    	if(!(conn.getChildren().get(relLabelIndex) instanceof Label)) return;
    	
    	Label relLabel = (Label) conn.getChildren().get(relLabelIndex);
    	conn.remove(relLabel);
    	arep.setFigure(conn);
    }
    

}